/*
    sntp.c

    Created on: 2014. 12. 15.
        Author: Administrator
*/


#include <string.h>

#include "sntp.h"
#include "socket.h"

ntpformat NTPformat;
datetime Nowdatetime;
uint8_t ntpmessage[48];
uint8_t *data_buf;
uint8_t NTP_SOCKET;
uint8_t time_zone;
uint16_t ntp_retry_cnt = 0; //counting the ntp retry number

/*
    00)UTC-12:00 Baker Island, Howland Island (both uninhabited)
    01) UTC-11:00 American Samoa, Samoa
    02) UTC-10:00 (Summer)French Polynesia (most), United States (Aleutian Islands, Hawaii)
    03) UTC-09:30 Marquesas Islands
    04) UTC-09:00 Gambier Islands;(Summer)United States (most of Alaska)
    05) UTC-08:00 (Summer)Canada (most of British Columbia), Mexico (Baja California)
    06) UTC-08:00 United States (California, most of Nevada, most of Oregon, Washington (state))
    07) UTC-07:00 Mexico (Sonora), United States (Arizona); (Summer)Canada (Alberta)
    08) UTC-07:00 Mexico (Chihuahua), United States (Colorado)
    09) UTC-06:00 Costa Rica, El Salvador, Ecuador (Galapagos Islands), Guatemala, Honduras
    10) UTC-06:00 Mexico (most), Nicaragua;(Summer)Canada (Manitoba, Saskatchewan), United States (Illinois, most of Texas)
    11) UTC-05:00 Colombia, Cuba, Ecuador (continental), Haiti, Jamaica, Panama, Peru
    12) UTC-05:00 (Summer)Canada (most of Ontario, most of Quebec)
    13) UTC-05:00 United States (most of Florida, Georgia, Massachusetts, most of Michigan, New York, North Carolina, Ohio, Washington D.C.)
    14) UTC-04:30 Venezuela
    15) UTC-04:00 Bolivia, Brazil (Amazonas), Chile (continental), Dominican Republic, Canada (Nova Scotia), Paraguay,
    16) UTC-04:00 Puerto Rico, Trinidad and Tobago
    17) UTC-03:30 Canada (Newfoundland)
    18) UTC-03:00 Argentina; (Summer) Brazil (Brasilia, Rio de Janeiro, Sao Paulo), most of Greenland, Uruguay
    19) UTC-02:00 Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands
    20) UTC-01:00 Portugal (Azores), Cape Verde
    21) UTC&#177;00:00 Cote d'Ivoire, Faroe Islands, Ghana, Iceland, Senegal; (Summer) Ireland, Portugal (continental and Madeira)
    22) UTC&#177;00:00 Spain (Canary Islands), Morocco, United Kingdom
    23) UTC+01:00 Angola, Cameroon, Nigeria, Tunisia; (Summer)Albania, Algeria, Austria, Belgium, Bosnia and Herzegovina,
    24) UTC+01:00 Spain (continental), Croatia, Czech Republic, Denmark, Germany, Hungary, Italy, Kinshasa, Kosovo,
    25) UTC+01:00 Macedonia, France (metropolitan), the Netherlands, Norway, Poland, Serbia, Slovakia, Slovenia, Sweden, Switzerland
    26) UTC+02:00 Libya, Egypt, Malawi, Mozambique, South Africa, Zambia, Zimbabwe, (Summer)Bulgaria, Cyprus, Estonia,
    27) UTC+02:00 Finland, Greece, Israel, Jordan, Latvia, Lebanon, Lithuania, Moldova, Palestine, Romania, Syria, Turkey, Ukraine
    28) UTC+03:00 Belarus, Djibouti, Eritrea, Ethiopia, Iraq, Kenya, Madagascar, Russia (Kaliningrad Oblast), Saudi Arabia,
    29) UTC+03:00 South Sudan, Sudan, Somalia, South Sudan, Tanzania, Uganda, Yemen
    30) UTC+03:30 (Summer)Iran
    31) UTC+04:00 Armenia, Azerbaijan, Georgia, Mauritius, Oman, Russia (European), Seychelles, United Arab Emirates
    32) UTC+04:30 Afghanistan
    33) UTC+05:00 Kazakhstan (West), Maldives, Pakistan, Uzbekistan
    34) UTC+05:30 India, Sri Lanka
    35) UTC+05:45 Nepal
    36) UTC+06:00 Kazakhstan (most), Bangladesh, Russia (Ural: Sverdlovsk Oblast, Chelyabinsk Oblast)
    37) UTC+06:30 Cocos Islands, Myanmar
    38) UTC+07:00 Jakarta, Russia (Novosibirsk Oblast), Thailand, Vietnam
    39) UTC+08:00 China, Hong Kong, Russia (Krasnoyarsk Krai), Malaysia, Philippines, Singapore, Taiwan, most of Mongolia, Western Australia
    40) UTC+09:00 Korea, East Timor, Russia (Irkutsk Oblast), Japan
    41) UTC+09:30 Australia (Northern Territory);(Summer)Australia (South Australia))
    42) UTC+10:00 Russia (Zabaykalsky Krai); (Summer)Australia (New South Wales, Queensland, Tasmania, Victoria)
    43) UTC+10:30 Lord Howe Island
    44) UTC+11:00 New Caledonia, Russia (Primorsky Krai), Solomon Islands
    45) UTC+11:30 Norfolk Island
    46) UTC+12:00 Fiji, Russia (Kamchatka Krai);(Summer)New Zealand
    47) UTC+12:45 (Summer)New Zealand
    48) UTC+13:00 Tonga
    49) UTC+14:00 Kiribati (Line Islands)
*/
void get_seconds_from_ntp_server(uint8_t *buf, uint16_t idx) {
    tstamp seconds = 0;
    uint8_t i = 0;
    for (i = 0; i < 4; i++) {
        seconds = (seconds << 8) | buf[idx + i];
    }
    switch (time_zone) {
    case 0:
        seconds -=  12 * 3600;
        break;
    case 1:
        seconds -=  11 * 3600;
        break;
    case 2:
        seconds -=  10 * 3600;
        break;
    case 3:
        seconds -= (9 * 3600 + 30 * 60);
        break;
    case 4:
        seconds -=  9 * 3600;
        break;
    case 5:
    case 6:
        seconds -=  8 * 3600;
        break;
    case 7:
    case 8:
        seconds -=  7 * 3600;
        break;
    case 9:
    case 10:
        seconds -=  6 * 3600;
        break;
    case 11:
    case 12:
    case 13:
        seconds -= 5 * 3600;
        break;
    case 14:
        seconds -= (4 * 3600 + 30 * 60);
        break;
    case 15:
    case 16:
        seconds -=  4 * 3600;
        break;
    case 17:
        seconds -= (3 * 3600 + 30 * 60);
        break;
    case 18:
        seconds -=  3 * 3600;
        break;
    case 19:
        seconds -=  2 * 3600;
        break;
    case 20:
        seconds -=  1 * 3600;
        break;
    case 21:                            //�?
    case 22:
        break;
    case 23:
    case 24:
    case 25:
        seconds +=  1 * 3600;
        break;
    case 26:
    case 27:
        seconds +=  2 * 3600;
        break;
    case 28:
    case 29:
        seconds +=  3 * 3600;
        break;
    case 30:
        seconds += (3 * 3600 + 30 * 60);
        break;
    case 31:
        seconds +=  4 * 3600;
        break;
    case 32:
        seconds += (4 * 3600 + 30 * 60);
        break;
    case 33:
        seconds +=  5 * 3600;
        break;
    case 34:
        seconds += (5 * 3600 + 30 * 60);
        break;
    case 35:
        seconds += (5 * 3600 + 45 * 60);
        break;
    case 36:
        seconds +=  6 * 3600;
        break;
    case 37:
        seconds += (6 * 3600 + 30 * 60);
        break;
    case 38:
        seconds +=  7 * 3600;
        break;
    case 39:
        seconds +=  8 * 3600;
        break;
    case 40:
        seconds +=  9 * 3600;
        break;
    case 41:
        seconds += (9 * 3600 + 30 * 60);
        break;
    case 42:
        seconds +=  10 * 3600;
        break;
    case 43:
        seconds += (10 * 3600 + 30 * 60);
        break;
    case 44:
        seconds +=  11 * 3600;
        break;
    case 45:
        seconds += (11 * 3600 + 30 * 60);
        break;
    case 46:
        seconds +=  12 * 3600;
        break;
    case 47:
        seconds += (12 * 3600 + 45 * 60);
        break;
    case 48:
        seconds +=  13 * 3600;
        break;
    case 49:
        seconds +=  14 * 3600;
        break;

    }

    //calculation for date
    calcdatetime(seconds);
}

void SNTP_init(uint8_t s, uint8_t *ntp_server, uint8_t tz, uint8_t *buf) {
    NTP_SOCKET = s;

    NTPformat.dstaddr[0] = ntp_server[0];
    NTPformat.dstaddr[1] = ntp_server[1];
    NTPformat.dstaddr[2] = ntp_server[2];
    NTPformat.dstaddr[3] = ntp_server[3];

    time_zone = tz;

    data_buf = buf;

    uint8_t Flag;
    NTPformat.leap = 0;           /* leap indicator */
    NTPformat.version = 4;        /* version number */
    NTPformat.mode = 3;           /* mode */
    NTPformat.stratum = 0;        /* stratum */
    NTPformat.poll = 0;           /* poll interval */
    NTPformat.precision = 0;      /* precision */
    NTPformat.rootdelay = 0;      /* root delay */
    NTPformat.rootdisp = 0;       /* root dispersion */
    NTPformat.refid = 0;          /* reference ID */
    NTPformat.reftime = 0;        /* reference time */
    NTPformat.org = 0;            /* origin timestamp */
    NTPformat.rec = 0;            /* receive timestamp */
    NTPformat.xmt = 1;            /* transmit timestamp */

    Flag = (NTPformat.leap << 6) + (NTPformat.version << 3) + NTPformat.mode; //one byte Flag
    memcpy(ntpmessage, (void const*)(&Flag), 1);
}

int8_t SNTP_run(datetime *time) {
    uint16_t RSR_len;
    uint32_t destip = 0;
    uint16_t destport;
    uint16_t startindex = 40; //last 8-byte of data_buf[size is 48 byte] is xmt, so the startindex should be 40
#if 1
    // 20231019 taylor
    uint8_t addr_len;
#endif

    switch (getSn_SR(NTP_SOCKET)) {
    case SOCK_UDP:
        if ((RSR_len = getSn_RX_RSR(NTP_SOCKET)) > 0) {
            if (RSR_len > MAX_SNTP_BUF_SIZE) {
                RSR_len = MAX_SNTP_BUF_SIZE;    // if Rx data size is lager than TX_RX_MAX_BUF_SIZE
            }
#if 1
            // 20231019 taylor//teddy 240122
#if ((_WIZCHIP_ == 6100) || (_WIZCHIP_ == 6300))
            recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport, &addr_len);
#else
            recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport);
#endif
#else
            recvfrom(NTP_SOCKET, data_buf, RSR_len, (uint8_t *)&destip, &destport);
#endif

            get_seconds_from_ntp_server(data_buf, startindex);
            time->yy = Nowdatetime.yy;
            time->mo = Nowdatetime.mo;
            time->dd = Nowdatetime.dd;
            time->hh = Nowdatetime.hh;
            time->mm = Nowdatetime.mm;
            time->ss = Nowdatetime.ss;

            ntp_retry_cnt = 0;
            close(NTP_SOCKET);

            return 1;
        }

        if (ntp_retry_cnt < 0xFFFF) {
            if (ntp_retry_cnt == 0) { //first send request, no need to wait
#if 1
                // 20231016 taylor//teddy 240122
#if ((_WIZCHIP_ == 6100) || (_WIZCHIP_ == 6300))
                sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage), NTPformat.dstaddr, ntp_port, 4);
#else
                sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage), NTPformat.dstaddr, ntp_port);
#endif
#else
                sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage), NTPformat.dstaddr, ntp_port);
#endif
                ntp_retry_cnt++;
            } else { // send request again? it should wait for a while
                if ((ntp_retry_cnt % 0xFFF) == 0) { //wait time
#if 1
                    // 20231016 taylor//teddy 240122
#if ((_WIZCHIP_ == 6100) || (_WIZCHIP_ == 6300))
                    sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage), NTPformat.dstaddr, ntp_port, 4);
#else
                    sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage), NTPformat.dstaddr, ntp_port);
#endif
#else
                    sendto(NTP_SOCKET, ntpmessage, sizeof(ntpmessage), NTPformat.dstaddr, ntp_port);
#endif
#ifdef _SNTP_DEBUG_
                    printf("ntp retry: %d\r\n", ntp_retry_cnt);
#endif
                    ntp_retry_cnt++;
                }
            }
        } else { //ntp retry fail
            ntp_retry_cnt = 0;
#ifdef _SNTP_DEBUG_
            printf("ntp retry failed!\r\n");
#endif
            close(NTP_SOCKET);
        }
        break;
    case SOCK_CLOSED:
        socket(NTP_SOCKET, Sn_MR_UDP, ntp_port, 0);
        break;
    }
    // Return value
    // 0 - failed / 1 - success
    return 0;
}

void calcdatetime(tstamp seconds) {
    uint8_t yf = 0;
    uint8_t leap;
    tstamp n = 0, d = 0, total_d = 0, rz = 0;
    uint16_t y = 0, r = 0, yr = 0;
    signed long long yd = 0;

    n = seconds;
    total_d = seconds / (SECS_PERDAY);
    d = 0;
    uint32_t p_year_total_sec = SECS_PERDAY * 365;
    uint32_t r_year_total_sec = SECS_PERDAY * 366;

    while (1) {
        leap = 0;
        if (((EPOCH + r) % 400 == 0) ||
                (((EPOCH + r) % 100 != 0) && ((EPOCH + r) % 4 == 0))) {
            leap = 1;
        }

        if (leap) {
            if (n < r_year_total_sec) {
                break;
            }
            n -= r_year_total_sec;
            d += 366;
        } else {
            if (n < p_year_total_sec) {
                break;
            }
            n -= p_year_total_sec;
            d += 365;
        }

        r++;
        y++;
    }

    y += EPOCH;

    Nowdatetime.yy = y;

    yd = 0;
    yd = total_d - d;

    yf = 1;
    while (yd >= 28) {

        if (yf == 1 || yf == 3 || yf == 5 || yf == 7 || yf == 8 || yf == 10 || yf == 12) {
            yd -= 31;
            if (yd < 0) {
                break;
            }
            rz += 31;
        }

        if (yf == 2) {
            if (y % 400 == 0 || (y % 100 != 0 && y % 4 == 0)) {
                yd -= 29;
                if (yd < 0) {
                    break;
                }
                rz += 29;
            } else {
                yd -= 28;
                if (yd < 0) {
                    break;
                }
                rz += 28;
            }
        }
        if (yf == 4 || yf == 6 || yf == 9 || yf == 11) {
            yd -= 30;
            if (yd < 0) {
                break;
            }
            rz += 30;
        }
        yf += 1;

    }
    Nowdatetime.mo = yf;
    yr = total_d - d - rz;

    yr += 1;

    Nowdatetime.dd = yr;

    //calculation for time
    seconds = seconds % SECS_PERDAY;
    Nowdatetime.hh = seconds / 3600;
    Nowdatetime.mm = (seconds % 3600) / 60;
    Nowdatetime.ss = (seconds % 3600) % 60;

}

tstamp changedatetime_to_seconds(void) {
    tstamp seconds = 0;
    uint32_t total_day = 0;
    uint16_t i = 0, run_year_cnt = 0, l = 0;

    l = Nowdatetime.yy;//low


    for (i = EPOCH; i < l; i++) {
        if ((i % 400 == 0) || ((i % 100 != 0) && (i % 4 == 0))) {
            run_year_cnt += 1;
        }
    }

    total_day = (l - EPOCH - run_year_cnt) * 365 + run_year_cnt * 366;

    for (i = 1; i <= Nowdatetime.mo; i++) {
        if (i == 5 || i == 7 || i == 10 || i == 12) {
            total_day += 30;
        }
        if (i == 3) {
            if (l % 400 == 0 || l % 100 != 0 && l % 4 == 0) {
                total_day += 29;
            } else {
                total_day += 28;
            }
        }
        if (i == 2 || i == 4 || i == 6 || i == 8 || i == 9 || i == 11) {
            total_day += 31;
        }
    }

    seconds = (total_day + Nowdatetime.dd - 1) * 24 * 3600;
    seconds += Nowdatetime.ss;//seconds
    seconds += Nowdatetime.mm * 60; //minute
    seconds += Nowdatetime.hh * 3600; //hour

    return seconds;
}
